-------Hugo Hound's Vowel Sounds-------
A 4am crack                  2020-12-30
---------------------------------------

Name: Hugo Hound's Vowel Sounds: Long
  Vowels
Genre: educational
Year: 1993
Publisher: Micrograms
Platform: Apple //e or later (128K)
Media: 5.25-inch disk
Sides: 1
OS: custom
Previous cracks: none
Similar cracks:
  #796 Marty's Reading Workout
  #797 Marty's Family Reader

I acquired the manual with this disk.
It mentions a second disk, "Short
Vowels," which was separated from its
brethren and may be losts to the mists
of time. If you find it, it almost
certainly shares its copy protection
with this disk, so I hope this write-up
guides your way.

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  immediate disk read error

Locksmith Fast Disk Backup
  can't read track $00, sector $0C
  copy loads title screen then breaks
  to text page with "]ERROR D51"

EDD 4 bit copy (no sync, no count)
  works

Copy ][+ nibble editor
  T00,S0C exists (I searched for the
  raw nibble sequence "AA AB AE", which
  matches the second half of the track
  ("AA AA" -> $00) and the sector
  ("AB AE" -> $06 = logical sector $0C)
  in the address field

Disk Fixer
  setting "CHECKSUM ENABLED" to "NO"
  allows me to read T00,S0C

Why didn't COPYA work?
  intentionally corrupted sector on T00

Why didn't Locksmith FDB work?
  probably a run-time check to ensure
  that sector on T00 is corrupted,
  which it isn't, on my copy, because
  Locksmith Fast Disk Backup will just
  write out a standard sector of zeroes
  instead of reproducing the corruption

EDD worked. What does that tell us?
  Probably just a bad block check:
  unreadable sector = original,
  readable sector = unauthorized copy

Next steps:

  1. Use a sector editor to search for
     obvious signs of sector reads
  2. If that fails, trace the boot
  3. I don't know, go feed the ducks or
     something (*)

(*) not bread though, it's bad for them

                   ~

               Chapter 1
      Unexpected Operating System
         Found In Bagging Area


The disk appears to boot directly to
the program, without loading any known
operating system first. But while I was
poking around the corrupted track 0, I
noticed a normal ProDOS catalog. And in
fact, I can boot from my ProDOS hard
drive and catalog this disk!

                 --v--

]PR#7
...
]CAT,S6,D1

/BOOT

 NAME           TYPE  BLOCKS  MODIFIED

 PRODOS          SYS       7  <NO DATE>
 HUGOTITLESCR2   BIN      33  <NO DATE>
 LOWDOS          BIN       6  <NO DATE>
 FONT1.DHR       BIN       7  <NO DATE>
 UTL             BIN       9  <NO DATE>
 MAIN            BIN      23  <NO DATE>
 FONT2.DHR       BIN       8  <NO DATE>
 MAINWIN         BIN      20  <NO DATE>
 DATA            TXT       4  16-JUN-92
 SLIDES1A        BIN      22  <NO DATE>
 SLIDES1B        BIN      20  <NO DATE>
 SLIDES2A        BIN      23  <NO DATE>
 SLIDES2B        BIN      23  <NO DATE>
 PRINTUTL        BIN       6  <NO DATE>
 ENDANIM         BIN      22  <NO DATE>
 DISK            BIN       3  <NO DATE>

BLOCKS FREE:   38     BLOCKS USED:  242

                 --^--

Anyway, might prove useful, especially
being able to cross-reference sectors
to files and finding out where they're
loaded in memory.

Onward!

My non-working copy prints an error
message. Let's see if we can find it.
Turning to my trusty Disk Fixer sector
editor, I search for the ASCII string
"ERROR" and find it in T08,S05!

Copy II Plus recognizes this disk as
ProDOS, and the "disk map" says that
T05,S05 is part of file "MAIN".

                 --v--

DISK MAP                SLOT 6  DRIVE 1
/BOOT/MAIN

   TRACK           1               2
   0123456789ABCDEF0123456789ABCDEF012

S0 .........***.......................
EE .........***.......................
CD .........***.......................
TC .........***.......................
OB .........***.......................
RA .........***.......................
 9 .........***.......................
 8 .........***.......................
 7 .........**........................
 6 .........**........................
 5 ........***........................
 4 ........***........................
 3 ........***........................
 2 ........***........................
 1 ........***........................
 F ........***........................

                 --^--

Booting my ProDOS hard drive, I can
BLOAD that file into memory and start
tracing. According to the full CATALOG
command (not shown), the file "MAIN" is
loaded at address $6000.

]PR#7
...
]PREFIX /BOOT
]BLOAD MAIN
]CALL -151

*6000L

6000-   4C AB 60    JMP   $60AB

*60ABL

; read/write RAM bank 1
60AB-   AD 8B C0    LDA   $C08B
60AE-   AD 8B C0    LDA   $C08B
60B1-   8D 08 C0    STA   $C008

; could be anything, but given the
; current program counter, I'm guessing
; this is the address $6103, which is
; nearby
60B4-   A9 03       LDA   #$03
60B6-   8D 07 08    STA   $0807
60B9-   A9 61       LDA   #$61
60BB-   8D 08 08    STA   $0808

; don't know
60BE-   A9 01       LDA   #$01
60C0-   8D 72 0A    STA   $0A72

; don't know
60C3-   20 03 08    JSR   $0803

; set mainmem (as opposed to auxmem)
60C6-   8D 08 C0    STA   $C008

; read/write RAM bank 2
60C9-   AD 83 C0    LDA   $C083
60CC-   AD 83 C0    LDA   $C083

; don't know
60CF-   20 0F E0    JSR   $E00F

; 40 column mode
60D2-   8D 0C C0    STA   $C00C

; text mode
60D5-   2C 51 C0    BIT   $C051

; don't know
60D8-   20 15 E0    JSR   $E015

; copy string to text page
60DB-   A0 0A       LDY   #$0A
60DD-   B9 F8 60    LDA   $60F8,Y
60E0-   99 D0 07    STA   $07D0,Y
60E3-   88          DEY
60E4-   10 F7       BPL   $60DD
...
60F8-   "]ERROR D51"
6102-   FF

Oh, this is the Badlands! That's the
error message I found earlier in the
sector editor. I don't know what's going
on, but we don't want to be here.

Which tells me we are definitely on the
right track. Let's back up and figure
out how the original disk avoids ending
up here.

                   ~

               Chapter 2
     When They Go Low, We Go High


At $60B4 we're setting some parameters
and calling a routine at $0803. What's
at $0803? According to the ProDOS
metadata, "LOWDOS" is loaded at $0800.
Let's see what the heck "LOWDOS" is.

*BLOAD LOWDOS
*803L

0803-   4C 5A 0A    JMP   $0A5A

*A5AL

0A5A-   A2 08       LDX   #$08
0A5C-   A9 00       LDA   #$00
0A5E-   9D 27 08    STA   $0827,X
0A61-   CA          DEX
0A62-   10 FA       BPL   $0A5E

; turn on slot 6 drive motor
0A64-   2C E9 C0    BIT   $C0E9

; initialize... something
0A67-   A9 01       LDA   #$01
0A69-   8D 31 08    STA   $0831
0A6C-   A9 00       LDA   #$00
0A6E-   8D 32 08    STA   $0832
0A71-   A9 02       LDA   #$02
0A73-   8D 43 08    STA   $0843
0A76-   A9 00       LDA   #$00
0A78-   8D 44 08    STA   $0844
0A7B-   A9 0D       LDA   #$0D
0A7D-   A0 00       LDY   #$00
0A7F-   20 45 08    JSR   $0845

*845L

0845-   8D AF 09    STA   $09AF
0848-   8C AE 09    STY   $09AE

; memory fiddling (not shown)
084B-   20 B4 09    JSR   $09B4

; do something
084E-   AE 41 08    LDX   $0841
0851-   BD 28 10    LDA   $1028,X
0854-   20 6E 08    JSR   $086E     (1)

; increment something
0857-   EE AF 09    INC   $09AF

; and do the same thing again, but
; differently
085A-   AE 41 08    LDX   $0841
085D-   BD 30 10    LDA   $1030,X
0860-   20 6E 08    JSR   $086E     (2)

; and we're done
0863-   A9 00       LDA   #$00
0865-   8D AE 09    STA   $09AE
0868-   A9 0D       LDA   #$0D
086A-   8D AF 09    STA   $09AF
086D-   60          RTS

Here's the tables at $1028 and $1030:

*1028.

1028- 00 04 08 0C 01 05 09 0D
1030- 02 06 0A 0E 03 07 0B 0F

OK, I'm beginning to see what's going
on here. This routine looks like it's
loading a ProDOS "block" -- two
consecutive sectors on disk, where by
"consecutive," I mean "consecutive in
the ProDOS skewing order." $0841 holds
the index into the 8-item arrays at
$1028 and $1030, which map logical to
physical sectors.

If I'm right, that means that $086E is
the main entry point to read a sector
from disk.

*86EL

086E-   85 EC       STA   $EC

; reset data latch
0870-   AD EE C0    LDA   $C0EE
0873-   A9 03       LDA   #$03
0875-   8D 30 08    STA   $0830
0878-   20 8D 08    JSR   $088D

*88DL

; set up death counter
088D-   A9 00       LDA   #$00
088F-   8D 8C 08    STA   $088C
0892-   CE 8C 08    DEC   $088C
0895-   D0 03       BNE   $089A

; if death counter hits 0, JSR(?!) here
; (more on this later)
0897-   20 81 09    JSR   $0981

; another death counter
089A-   A9 00       LDA   #$00
089C-   85 FC       STA   $FC
089E-   88          DEY
089F-   D0 07       BNE   $08A8
08A1-   C6 FC       DEC   $FC
08A3-   D0 03       BNE   $08A8

; and again, if that death counter hits
; 0, JSR to the same place as $0887
08A5-   20 81 09    JSR   $0981

I'm beginning to suspect that $0981
doesn't ever return, but we'll get to
that in a minute.

; find address prologue (D5 AA 96)
08A8-   AD EC C0    LDA   $C0EC
08AB-   10 FB       BPL   $08A8
08AD-   C9 D5       CMP   #$D5
08AF-   D0 ED       BNE   $089E
08B1-   AD EC C0    LDA   $C0EC
08B4-   10 FB       BPL   $08B1
08B6-   C9 AA       CMP   #$AA
08B8-   D0 EE       BNE   $08A8
08BA-   AD EC C0    LDA   $C0EC
08BD-   10 FB       BPL   $08BA
08BF-   C9 96       CMP   #$96
08C1-   D0 E5       BNE   $08A8

; parse address field, store in $083D+
08C3-   A0 03       LDY   #$03
08C5-   A9 00       LDA   #$00
08C7-   8D 3C 08    STA   $083C
08CA-   AD EC C0    LDA   $C0EC
08CD-   10 FB       BPL   $08CA
08CF-   2A          ROL
08D0-   85 F9       STA   $F9
08D2-   AD EC C0    LDA   $C0EC
08D5-   10 FB       BPL   $08D2
08D7-   25 F9       AND   $F9
08D9-   99 3D 08    STA   $083D,Y
08DC-   4D 3C 08    EOR   $083C
08DF-   88          DEY
08E0-   10 E5       BPL   $08C7
08E2-   A8          TAY
08E3-   D0 AD       BNE   $0892
08E5-   AD 3F 08    LDA   $083F
08E8-   C5 EB       CMP   $EB

; success path branches (if address
; field checksum verifies)
08EA-   F0 0B       BEQ   $08F7

; failure path -- recalibrate the drive
; and try again to find the right track
; (not shown)
08EC-   0A          ASL
08ED-   85 ED       STA   $ED
08EF-   A5 EB       LDA   $EB
08F1-   20 E8 09    JSR   $09E8
08F4-   4C 92 08    JMP   $0892

; execution continues here (from $08EA)
; check if we got the sector we wanted,
; otherwise branch back and try again
08F7-   AD 3E 08    LDA   $083E
08FA-   C5 EC       CMP   $EC
08FC-   D0 94       BNE   $0892
08FE-   60          RTS

Continuing from $087B...

; read data field (prologue, data, and
; epilogue -- not shown, but it sets
; the carry on failure and clears it on
; success, like DOS 3.3)
087B-   20 FF 08    JSR   $08FF

; branch forward on success
087E-   90 08       BCC   $0888

; decrement death counter and try again
0880-   CE 30 08    DEC   $0830
0883-   D0 F3       BNE   $0878

; once again, we end up calling $0981
; after the death counter hits 0
0885-   20 81 09    JSR   $0981

; execution continues here (from $087E)
; this routine finishes the nibble-to-
; byte conversion of the raw nibbles
; that were read earlier in $08FF
; (not shown)
0888-   20 9B 09    JSR   $099B
088B-   60          RTS

So... we're reading sectors, more or
less the same way that DOS 3.3 reads
sectors. The strangest part is that any
fatal error ends up JSR'ing to $0981.
What's at $0981?

*981L

; turn off slot 6 drive motor
0981-   2C E8 C0    BIT   $C0E8

; reset stack pointer (so I was right,
; this routine never returns to the
; caller)
0984-   A2 FF       LDX   #$FF
0986-   9A          TXS

; jump to "fatal error" vector
0987-   4C 06 08    JMP   $0806

But wait! We set that vector before
calling LOWDOS -- all the way back at
$60B4:

60B4-   A9 03       LDA   #$03
60B6-   8D 07 08    STA   $0807
60B9-   A9 61       LDA   #$61
60BB-   8D 08 08    STA   $0808

Immediately after setting that fatal
error vector, we called LOWDOS to read
an unreadable block that spans T00,S0C:

60C3-   20 03 08    JSR   $0803

And that's the key to this protection
scheme: the "success" path routes
through the fatal error vector at $0806
and continues to the start of the game
at $6103. If LOWDOS doesn't encounter
an error, it returns to... what? Well,
the "JSR $0803" at $60C3 eventually
returns gracefully, and we continue to
the next instruction, which as we saw
before was The Badlands.

Which is exactly the behavior I saw on
my non-working copy.

                   ~

               Chapter 3
     One Byte(*) To Rule Them All
    And In The Darkness Patch Them


(*) not guaranteed, actual count may
    vary

The protection routine has no side
effects. We can bypass the entire thing
by jumping to the "error" handler
directly at $6000, instead of jumping to
the protection routine.

T08,S0A,$01: AB 60 -> 03 61

]PR#6
...works, and it is glorious...

Quod erat liberandum.

---------------------------------------
A 4am crack                    No. 2301
------------------EOF------------------
